// 主线程代码
import { ipcMain } from 'electron';
import createDHWorker from './workers/DHWorker?nodeWorker';
import createRSAWorker from './workers/RSAWorker?nodeWorker';
import createAESWorker from './workers/AESWorker?nodeWorker';
import fs from 'node:fs/promises';
import { resolve } from 'node:path';
import { existsSync, mkdirSync } from 'node:fs';

const dhWorker = createDHWorker({ workerData: '' });
const rsaWorker = createRSAWorker({ workerData: '' });

//共享密钥映射表,<userId,shareKey>
const shareKeyMap: Map<string, string> = new Map();

const aesWorker = createAESWorker({ workerData: '' });

// DH和RSA密钥对的类型
type DH_Key_Pair = {
  publicKey: string; //pem格式
  privateKey: string; //pem格式
};

type RSA_Key_Pair = {
  publicKey: string; //pem格式
  privateKey: string; //pem格式
};

// 初始化DH和RSA密钥对为空
let dhKeyPair: DH_Key_Pair = {
  publicKey: '',
  privateKey: ''
};

let rsaKeyPair: RSA_Key_Pair = {
  publicKey: '',
  privateKey: ''
};

ipcMain.on('aes-encrypt', async (event, friendId, msg) => {
  const shareKey = shareKeyMap.get(friendId) as string;
  if (!shareKey) {
    console.log('找不到共享密钥');
    event.sender.send('aes-encrypt', '');
  }
  event.sender.send('aes-encrypt', await aesEncrypt(shareKey, msg));
});

const aesEncrypt = async (
  key: string,
  plainText: string
): Promise<{
  iv: string;
  encrypted: string;
}> => {
  const encrypted = await new Promise<{
    iv: string;
    encrypted: string;
  }>((resolve, _reject) => {
    aesWorker.postMessage({
      method: 'encrypt',
      key: key,
      plainText: plainText
    });
    aesWorker.once('message', (msg) => {
      resolve(msg.encrypted);
    });
  });
  return encrypted;
};

ipcMain.on('aes-decrypt', async (event, senderId, iv, encrypted) => {
  const shareKey = shareKeyMap.get(senderId) as string;
  if (!shareKey) {
    event.sender.send('aes-decrypt', '解密失败，找不到共享密钥');
  }
  console.log('解密时使用的aes密钥：', shareKey);
  try {
    event.sender.send('aes-decrypt', await aesDecrypt(shareKey, iv, encrypted));
  } catch (error) {
    event.sender.send('aes-decrypt', '解密失败，密钥错误');
  }
});

const aesDecrypt = async (
  key: string,
  iv: string,
  encrypted: string
): Promise<{
  encrypted: string;
}> => {
  return await new Promise<{ iv: string; encrypted: string }>(
    (resolve, _reject) => {
      aesWorker.postMessage({
        method: 'decrypt',
        key: key,
        iv: iv,
        encrypted: encrypted
      });
      aesWorker.once('message', (msg) => {
        resolve(msg.decrypted);
      });
    }
  );
};

ipcMain.on('get-public-key', async (event, _) => {
  // 签名该哈希
  rsaWorker.postMessage({
    method: 'sign-data',
    rsaPrivateKey: rsaKeyPair.privateKey,
    data: dhKeyPair.publicKey
  });

  rsaWorker.once('message', (data) => {
    if (data.hasOwnProperty('signature')) {
      event.sender.send('get-public-key', {
        rsaKeyPair: {
          publicKey: rsaKeyPair.publicKey
        },
        dhKeyPair: {
          publicKey: dhKeyPair.publicKey
        },
        sign: data.signature
      });
    }
  });
});

// 处理dh密钥协商
ipcMain.on(
  'key-negotiation',
  async (
    event,
    senderDHPublicKey: string,
    senderRsaPublicKey: string,
    senderSign: string,
    userId: string
  ) => {
    //验证签名
    const isValid = await validSign(
      senderRsaPublicKey,
      senderDHPublicKey,
      senderSign
    );

    if (isValid === false) {
      console.log('签名验证失败');
      event.sender.send('key-negotiation', {
        isNegotiated: false,
        shareKey: ''
      });
    } else {
      console.log('签名验证成功');
      //进行密钥协商
      const negotiationResult = keyNegotiation(
        dhKeyPair.publicKey,
        dhKeyPair.privateKey,
        senderDHPublicKey
      );

      negotiationResult
        .then((result) => {
          shareKeyMap.set(userId, result.shareKey);
          console.log(
            '主进程-写入Map后获取到的用户ID:',
            shareKeyMap.get(userId)
          );
          console.log('加密模块-密钥协商成功：', result);
          event.sender.send('key-negotiation', result.isNegotiated);
        })
        .catch((err) => {
          console.log('密钥协商函数错误：', err);
          event.sender.send('key-negotiation', {
            isNegotiated: false,
            shareKey: ''
          });
        });
    }
  }
);

/**
 * 初始化密钥数据
 * @param userId 用户id
 * @returns boolean 初始化结果
 */
export const initKeypair = async (userId: string): Promise<boolean> => {
  const dirInit = ensureDir(resolve(`${userId}`));

  if (!dirInit) {
    console.log('用户文件夹创建失败');
    return false;
  }

  const userFile = {
    rsaPublicKey: resolve(`${userId}`, `rsa-public.key`),
    rsaPrivateKey: resolve(`${userId}`, `rsa-private.key`)
  };

  try {
    //尝试读取rsa密钥文件
    const promises: [Promise<string>, Promise<string>] = [
      fs.readFile(userFile.rsaPublicKey, { encoding: 'utf-8' }),
      fs.readFile(userFile.rsaPrivateKey, { encoding: 'utf-8' })
    ];

    //并发读取文件
    const [rsaPublicKey, rsaPrivateKey] = await Promise.all(promises);

    //把读取到的结果写入缓存
    rsaKeyPair = {
      publicKey: rsaPublicKey,
      privateKey: rsaPrivateKey
    };

    //判断rsa密钥对是否已经写入并且值存在
    if (!rsaKeyPair.publicKey || !rsaKeyPair.privateKey) {
      throw new Error('读取RSA密钥对失败');
    }

    // if (!dhKeyPair.publicKey || !dhKeyPair.privateKey) {
    //   throw new Error('生成DH密钥对失败');
    // }

    console.log('文件读取成功');
    return true;
  } catch (error: any) {
    //判断读取出错是不是除了“不存在文件”以外的错误
    if (error.code === 'ENOENT') {
      console.log('不存在文件夹，正则创建');
      //判断文件不存在，生成密钥对
      const _rsaKeyPair = await createRSAKeyPair();

      rsaKeyPair = {
        publicKey: _rsaKeyPair.publicKey,
        privateKey: _rsaKeyPair.privateKey
      };

      //判断rsa密钥对是否已经写入并且值存在
      if (!rsaKeyPair.publicKey || !rsaKeyPair.privateKey) {
        throw new Error('生成RSA密钥对失败');
      }

      await writeFiles(userFile, rsaKeyPair);

      console.log('密钥对生成完毕');
      return true;
    }
    return false;
  } finally {
    try {
      const dhKeyPairTemp = await createDHKeyPair();
      dhKeyPair = {
        publicKey: dhKeyPairTemp.publicKey,
        privateKey: dhKeyPairTemp.privateKey
      };
    } catch (error: any) {
      console.log('DH密钥生成失败：', error);
    }
  }
};

/**
 * 写入用户文件
 * @param userFile 用户文件
 * @param rsaKeyPair RSA密钥对
 * @returns boolean 写入结果
 */
const writeFiles = async (
  userFile: {
    rsaPublicKey: string;
    rsaPrivateKey: string;
  },
  rsaKeyPair: RSA_Key_Pair
) => {
  try {
    const promises = [
      fs.writeFile(userFile.rsaPublicKey, rsaKeyPair.publicKey, {
        encoding: 'utf-8'
      }),
      fs.writeFile(userFile.rsaPrivateKey, rsaKeyPair.privateKey, {
        encoding: 'utf-8'
      })
    ];

    await Promise.all(promises);
    console.log('文件写入完成');
    return true;
  } catch (e) {
    console.log('文件写入错误：', e);
    return false;
  }
};

/**
 * 确认文件夹存在，不存在则创建文件夹
 * @param dirPath 文件夹路径
 */
const ensureDir = (dirPath: string): boolean => {
  // 路径是否存在，存在则返回true
  if (existsSync(dirPath)) {
    return true;
  }

  // 不存在则创建文件夹
  mkdirSync(dirPath, { recursive: true });

  return existsSync(dirPath);
};

/**
 * 验证签名
 * @param rsaPublicKey rsa公钥
 * @param dataHash 数据哈希
 * @param sign 签名值
 * @returns boolean 验证结果
 */
const validSign = (rsaPublicKey: string, dataHash: string, sign: string) => {
  return new Promise<boolean>((resolve, reject) => {
    rsaWorker.postMessage({
      method: 'verify-signature',
      rsaPublicKey: rsaPublicKey,
      data: dataHash,
      signature: sign
    });
    rsaWorker.once('message', (msg) => {
      if (msg.hasOwnProperty('isValid')) {
        resolve(true);
      } else {
        reject(false);
      }
    });
  });
};

/**
 * 密钥协商
 * @param dhPublicKey dh公钥
 * @param dhPrivateKey dh私钥
 * @param otherDHPublicKey 对方的dh公钥
 * @returns 协商信息
 */
const keyNegotiation = (
  dhPublicKey: string,
  dhPrivateKey: string,
  otherDHPublicKey: string
) => {
  return new Promise<{ isNegotiated: boolean; shareKey: string }>(
    async (resolve, reject) => {
      try {
        const shareKey = await new Promise<string>((res, rej) => {
          dhWorker.postMessage({
            method: 'key-negotiation',
            dhPrivateKey: dhPrivateKey,
            dhPublicKey: dhPublicKey,
            otherPublicKey: otherDHPublicKey
          });

          //在上面验证完签名后才进行协商
          dhWorker.once('message', (msg) => {
            if (msg.hasOwnProperty('shareKey')) {
              res(msg.shareKey);
            } else {
              rej('密钥协商失败');
            }
          });
        });
        //返回最外层Promise
        resolve({
          isNegotiated: true,
          shareKey
        });
      } catch (error) {
        console.log('加密模块-密钥协商失败:', error);
        reject({
          isNegotiated: false,
          shareKey: ''
        });
      }
      resolve({
        isNegotiated: false,
        shareKey: ''
      });
    }
  );
};

/**
 * 生成DH密钥对
 * @return {Promise} DH密钥对
 */
const createDHKeyPair = async (): Promise<DH_Key_Pair> => {
  return await new Promise<DH_Key_Pair>((resolve, reject) => {
    dhWorker.postMessage({ method: 'getKeyPair' });
    dhWorker.once('message', (msg) => {
      console.log('函数消息-dh密钥对生成完成');
      resolve(msg);
    });
    dhWorker.on('error', (err) => {
      reject(err);
    });
  });
};

// 生成RSA密钥对的方法
const createRSAKeyPair = async (): Promise<RSA_Key_Pair> => {
  console.log('已触发函数：createRSAKeyPair');
  const rsaKeyPair = await new Promise<RSA_Key_Pair>((resolve, reject) => {
    rsaWorker.postMessage({ method: 'getKeyPair' });
    rsaWorker.once('message', (msg) => {
      console.log('函数消息-rsa密钥对生成完成：', msg);
      resolve(msg);
    });
    rsaWorker.on('error', (err) => {
      reject(err);
    });
  });
  return rsaKeyPair;
};
